package com.liruo.idworker.distributed.twitter;

import com.liruo.idworker.distributed.twitter.strategy.TimestampStrategy;
import com.liruo.idworker.exception.OutOfRangeException;
import com.liruo.treasureattic.common.util.DefaultTimeUtils;
import lombok.extern.slf4j.Slf4j;

import java.time.Instant;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.function.Supplier;

/**
 * @Author:liruo
 * @Date:2023-02-07-15:24:54
 * @Desc
 */
@Slf4j
public  class DefaultTimestampStrategy implements TimestampStrategy {
    private final long toBeSubtractedTimestamp;
    private final boolean isSecondMode;
    private final Supplier<Long> nowTimestamp;

    public DefaultTimestampStrategy() {
        this(Instant.now().toEpochMilli());
    }
    public DefaultTimestampStrategy(LocalDateTime toBeSubtractedTimestamp) {
        this(DefaultTimeUtils.toInstant(toBeSubtractedTimestamp).toEpochMilli());
    }
    public DefaultTimestampStrategy(Date toBeSubtractedTimestamp) {
        this(DefaultTimeUtils.toInstant(toBeSubtractedTimestamp).toEpochMilli());
    }
    public DefaultTimestampStrategy(long toBeSubtractedTimestamp) {
        this.toBeSubtractedTimestamp = toBeSubtractedTimestamp;
        this.isSecondMode = false;
        this.nowTimestamp = ()->Instant.now().toEpochMilli();
        init();
    }
    public DefaultTimestampStrategy(long toBeSubtractedTimestamp, boolean isSecondMode) {
        this.toBeSubtractedTimestamp = toBeSubtractedTimestamp;
        this.isSecondMode = isSecondMode;
        this.nowTimestamp = isSecondMode ? ()->Instant.now().getEpochSecond() : ()->Instant.now().toEpochMilli();
        init();
    }
    private void init(){
        if(log.isDebugEnabled()){
            log.debug("toBeSubtractedTimestamp is {} -- {}",
                    isSecondMode ? 
                            DefaultTimeUtils.formatS(toBeSubtractedTimestamp) : 
                            DefaultTimeUtils.format(toBeSubtractedTimestamp),
                    toBeSubtractedTimestamp
            );
        }
    }


    @Override
    public long getTimestamp() {
        long now = nowTimestamp.get();
        long nowContrast = now - toBeSubtractedTimestamp;
        checkNowContrastRange(now, nowContrast);
        return nowContrast;
    }

    private void checkNowContrastRange(long now, long nowContrast) {
        if(nowContrast > TimestampStrategy.MAX_TIMESTAMP_DIFF){
            String nowS = DefaultTimeUtils.formatS(now);
            
            String mode;
            String toBeSubtractedTimestampS;
            if(isSecondMode){
                mode = "second";
                toBeSubtractedTimestampS = DefaultTimeUtils.formatS(toBeSubtractedTimestamp);
            }else{
                mode = "milli";
                toBeSubtractedTimestampS = DefaultTimeUtils.format(toBeSubtractedTimestamp);
            }
            throw new OutOfRangeException(
                    "timestamp[%s mode] is used out, max value=%d, toBeSubtractedTimestamp=[%s===%d], now=[%s===%d]"
                            .formatted(mode, TimestampStrategy.MAX_TIMESTAMP_DIFF,
                                    toBeSubtractedTimestampS, toBeSubtractedTimestamp,
                                    nowS, now)
            );
        }
    }
}
